home *** CD-ROM | disk | FTP | other *** search
/ Amiga Games Extra 1996 September / Amiga Games Extra CD-ROM 9-1996.iso / userbox / publicdomain / vim-4.2 / src / gui_x11.c < prev    next >
C/C++ Source or Header  |  1996-06-16  |  58KB  |  2,379 lines

  1. /* vi:set ts=4 sw=4:
  2.  *
  3.  * VIM - Vi IMproved            by Bram Moolenaar
  4.  *                                GUI/Motif support by Robert Webb
  5.  *
  6.  * Do ":help uganda"  in Vim to read copying and usage conditions.
  7.  * Do ":help credits" in Vim to see a list of people who contributed.
  8.  */
  9.  
  10. #include <X11/keysym.h>
  11. #include <X11/Xatom.h>
  12. #include <X11/StringDefs.h>
  13. #include <X11/Intrinsic.h>
  14. #include <X11/Shell.h>
  15.  
  16. #include "vim.h"
  17. #include "globals.h"
  18. #include "proto.h"
  19. #include "option.h"
  20. #include "ops.h"
  21.  
  22. #define VIM_NAME        "vim"
  23. #define VIM_CLASS        "Vim"
  24.  
  25. /* Default resource values */
  26. #define DFLT_FONT                "7x13"
  27. #define DFLT_MENU_BG_COLOR        "gray77"
  28. #define DFLT_MENU_FG_COLOR        "black"
  29. #define DFLT_SCROLL_BG_COLOR    "gray60"
  30. #define DFLT_SCROLL_FG_COLOR    "gray77"
  31.  
  32. Widget vimShell = (Widget)NULL;
  33.  
  34. static XtAppContext app_context;
  35. static Atom   Atom_WM_DELETE_WINDOW;
  36.  
  37. static Pixel gui_x11_get_color            __ARGS((char_u *));
  38. static void  gui_x11_set_color            __ARGS((GC, Pixel));
  39. static void  gui_x11_set_font             __ARGS((GC, Font));
  40. static void  gui_x11_check_copy_area      __ARGS((void));
  41. static void  gui_x11_update_menus_recurse __ARGS((GuiMenu *, int));
  42.  
  43. static void  gui_x11_request_selection_cb __ARGS((Widget, XtPointer,
  44.                                                   Atom *, Atom *, XtPointer,
  45.                                                   long_u *, int *));
  46.  
  47. static Boolean  gui_x11_convert_selection_cb __ARGS((Widget, Atom *, Atom *, 
  48.                                                       Atom *, XtPointer *,
  49.                                                      long_u *, int *));
  50.  
  51. static void  gui_x11_lose_ownership_cb __ARGS((Widget, Atom *));
  52.  
  53. static void  gui_x11_wm_protocol_handler __ARGS((Widget, XtPointer,
  54.                                             XEvent *, Boolean *));
  55.  
  56. static void gui_x11_invert_area         __ARGS((int, int, int, int));
  57. static void gui_x11_yank_selection      __ARGS((int, int, int, int));
  58. static void gui_x11_get_word_boundaries __ARGS((GuiSelection *, int, int));
  59. static int  gui_x11_get_line_end        __ARGS((int));
  60. static void gui_x11_update_selection    __ARGS((GuiSelection *, int, int, int,
  61.                                                 int));
  62.  
  63. #define char_class(c)    (c <= ' ' ? ' ' : iswordchar(c))
  64.  
  65. #define invert_rectangle(r, c, nr, nc)                                \
  66.             XFillRectangle(gui.dpy, gui.wid, gui.invert_gc,            \
  67.                     FILL_X(c), FILL_Y(r), (nc) * gui.char_width,    \
  68.                     (nr) * gui.char_height)
  69.  
  70.  
  71. static struct
  72. {
  73.     KeySym    key_sym;
  74.     char_u    vim_code0;
  75.     char_u    vim_code1;
  76. } special_keys[] =
  77. {
  78.     {XK_Up,            'k', 'u'},
  79.     {XK_Down,        'k', 'd'},
  80.     {XK_Left,        'k', 'l'},
  81.     {XK_Right,        'k', 'r'},
  82.  
  83.     {XK_F1,            'k', '1'},
  84.     {XK_F2,            'k', '2'},
  85.     {XK_F3,            'k', '3'},
  86.     {XK_F4,            'k', '4'},
  87.     {XK_F5,            'k', '5'},
  88.     {XK_F6,            'k', '6'},
  89.     {XK_F7,            'k', '7'},
  90.     {XK_F8,            'k', '8'},
  91.     {XK_F9,            'k', '9'},
  92.     {XK_F10,        'k', ';'},
  93.  
  94.     {XK_F11,        'F', '1'},
  95.     {XK_F12,        'F', '2'},
  96.     {XK_F13,        'F', '3'},
  97.     {XK_F14,        'F', '4'},
  98.     {XK_F15,        'F', '5'},
  99.     {XK_F16,        'F', '6'},
  100.     {XK_F17,        'F', '7'},
  101.     {XK_F18,        'F', '8'},
  102.     {XK_F19,        'F', '9'},
  103.     {XK_F20,        'F', 'A'},
  104.  
  105.     {XK_F21,        'F', 'B'},
  106.     {XK_F22,        'F', 'C'},
  107.     {XK_F23,        'F', 'D'},
  108.     {XK_F24,        'F', 'E'},
  109.     {XK_F25,        'F', 'F'},
  110.     {XK_F26,        'F', 'G'},
  111.     {XK_F27,        'F', 'H'},
  112.     {XK_F28,        'F', 'I'},
  113.     {XK_F29,        'F', 'J'},
  114.     {XK_F30,        'F', 'K'},
  115.  
  116.     {XK_F31,        'F', 'L'},
  117.     {XK_F32,        'F', 'M'},
  118.     {XK_F33,        'F', 'N'},
  119.     {XK_F34,        'F', 'O'},
  120.     {XK_F35,        'F', 'P'},        /* keysymdef.h defines up to F35 */
  121.  
  122.     {XK_Help,        '%', '1'},
  123.     {XK_Undo,        '&', '8'},
  124.     {XK_BackSpace,    'k', 'b'},
  125.     {XK_Insert,        'k', 'I'},
  126.     {XK_Delete,        'k', 'D'},
  127.     {XK_Home,        'k', 'h'},
  128.     {XK_End,        '@', '7'},
  129.     {XK_Prior,        'k', 'P'},
  130.     {XK_Next,        'k', 'N'},
  131.     {XK_Print,        '%', '9'},
  132.  
  133.     /* Keypad keys: */
  134. #ifdef XK_KP_Left
  135.     {XK_KP_Left,    'k', 'l'},
  136.     {XK_KP_Right,    'k', 'r'},
  137.     {XK_KP_Up,        'k', 'u'},
  138.     {XK_KP_Down,    'k', 'd'},
  139.     {XK_KP_Insert,    'k', 'I'},
  140.     {XK_KP_Delete,    'k', 'D'},
  141.     {XK_KP_Home,    'k', 'h'},
  142.     {XK_KP_End,        '@', '7'},
  143.     {XK_KP_Prior,    'k', 'P'},
  144.     {XK_KP_Next,    'k', 'N'},
  145. #endif
  146.  
  147.     /* End of list marker: */
  148.     {(KeySym)0,        0, 0}
  149. };
  150.  
  151. #define XtNboldColor        "boldColor"
  152. #define XtCBoldColor        "BoldColor"
  153. #define XtNitalicColor        "italicColor"
  154. #define XtCItalicColor        "ItalicColor"
  155. #define XtNunderlineColor    "underlineColor"
  156. #define XtCUnderlineColor    "UnderlineColor"
  157. #define XtNcursorColor        "cursorColor"
  158. #define XtCCursorColor        "CursorColor"
  159. #define XtNboldFont            "boldFont"
  160. #define XtCBoldFont            "BoldFont"
  161. #define XtNitalicFont        "italicFont"
  162. #define XtCItalicFont        "ItalicFont"
  163. #define XtNboldItalicFont    "boldItalicFont"
  164. #define XtCBoldItalicFont    "BoldItalicFont"
  165. #define XtNscrollbarWidth    "scrollbarWidth"
  166. #define XtCScrollbarWidth    "ScrollbarWidth"
  167. #define XtNmenuHeight        "menuHeight"
  168. #define XtCMenuHeight        "MenuHeight"
  169.  
  170. /* Resources for setting the foreground and background colors of menus */
  171. #define XtNmenuBackground    "menuBackground"
  172. #define XtCMenuBackground    "MenuBackground"
  173. #define XtNmenuForeground    "menuForeground"
  174. #define XtCMenuForeground    "MenuForeground"
  175.  
  176. /* Resources for setting the foreground and background colors of scrollbars */
  177. #define XtNscrollBackground    "scrollBackground"
  178. #define XtCScrollBackground    "ScrollBackground"
  179. #define XtNscrollForeground    "scrollForeground"
  180. #define XtCScrollForeground    "ScrollForeground"
  181.  
  182. /*
  183.  * X Resources:
  184.  */
  185. static XtResource vim_resources[] =
  186. {
  187.     {
  188.         XtNforeground,
  189.         XtCForeground,
  190.         XtRPixel,
  191.         sizeof(Pixel),
  192.         XtOffsetOf(Gui, norm_pixel),
  193.         XtRString,
  194.         XtDefaultForeground
  195.     },
  196.     {
  197.         XtNbackground,
  198.         XtCBackground,
  199.         XtRPixel,
  200.         sizeof(Pixel),
  201.         XtOffsetOf(Gui, back_pixel),
  202.         XtRString,
  203.         XtDefaultBackground
  204.     },
  205.     {
  206.         XtNboldColor,
  207.         XtCBoldColor,
  208.         XtRPixel,
  209.         sizeof(Pixel),
  210.         XtOffsetOf(Gui, bold_pixel),
  211.         XtRString,
  212.         XtDefaultForeground
  213.     },
  214.     {
  215.         XtNitalicColor,
  216.         XtCItalicColor,
  217.         XtRPixel,
  218.         sizeof(Pixel),
  219.         XtOffsetOf(Gui, ital_pixel),
  220.         XtRString,
  221.         XtDefaultForeground
  222.     },
  223.     {
  224.         XtNunderlineColor,
  225.         XtCUnderlineColor,
  226.         XtRPixel,
  227.         sizeof(Pixel),
  228.         XtOffsetOf(Gui, underline_pixel),
  229.         XtRString,
  230.         XtDefaultForeground
  231.     },
  232.     {
  233.         XtNcursorColor,
  234.         XtCCursorColor,
  235.         XtRPixel,
  236.         sizeof(Pixel),
  237.         XtOffsetOf(Gui, cursor_pixel),
  238.         XtRString,
  239.         XtDefaultForeground
  240.     },
  241.     {
  242.         XtNfont,
  243.         XtCFont,
  244.         XtRString,
  245.         sizeof(String *),
  246.         XtOffsetOf(Gui, dflt_font),
  247.         XtRImmediate,
  248.         XtDefaultFont
  249.     },
  250.     {
  251.         XtNboldFont,
  252.         XtCBoldFont,
  253.         XtRString,
  254.         sizeof(String *),
  255.         XtOffsetOf(Gui, dflt_bold_fn),
  256.         XtRImmediate,
  257.         ""
  258.     },
  259.     {
  260.         XtNitalicFont,
  261.         XtCItalicFont,
  262.         XtRString,
  263.         sizeof(String *),
  264.         XtOffsetOf(Gui, dflt_ital_fn),
  265.         XtRImmediate,
  266.         ""
  267.     },
  268.     {
  269.         XtNboldItalicFont,
  270.         XtCBoldItalicFont,
  271.         XtRString,
  272.         sizeof(String *),
  273.         XtOffsetOf(Gui, dflt_boldital_fn),
  274.         XtRImmediate,
  275.         ""
  276.     },
  277.     {
  278.         XtNgeometry,
  279.         XtCGeometry,
  280.         XtRString,
  281.         sizeof(String *),
  282.         XtOffsetOf(Gui, geom),
  283.         XtRImmediate,
  284.         ""
  285.     },
  286.     {
  287.         XtNreverseVideo,
  288.         XtCReverseVideo,
  289.         XtRBool,
  290.         sizeof(Bool),
  291.         XtOffsetOf(Gui, rev_video),
  292.         XtRImmediate,
  293.         (XtPointer) False
  294.     },
  295.     {
  296.         XtNborderWidth,
  297.         XtCBorderWidth,
  298.         XtRInt,
  299.         sizeof(int),
  300.         XtOffsetOf(Gui, border_width),
  301.         XtRImmediate,
  302.         (XtPointer) 2
  303.     },
  304.     {
  305.         XtNscrollbarWidth,
  306.         XtCScrollbarWidth,
  307.         XtRInt,
  308.         sizeof(int),
  309.         XtOffsetOf(Gui, scrollbar_width),
  310.         XtRImmediate,
  311.         (XtPointer) SB_DEFAULT_WIDTH 
  312.     },
  313.     {
  314.         XtNmenuHeight,
  315.         XtCMenuHeight,
  316.         XtRInt,
  317.         sizeof(int),
  318.         XtOffsetOf(Gui, menu_height),
  319.         XtRImmediate,
  320.         (XtPointer) MENU_DEFAULT_HEIGHT
  321.     },
  322.     {
  323.         XtNmenuForeground,
  324.         XtCMenuForeground,
  325.         XtRPixel,
  326.         sizeof(Pixel),
  327.         XtOffsetOf(Gui, menu_fg_pixel),
  328.         XtRString,
  329.         DFLT_MENU_FG_COLOR
  330.     },
  331.     {
  332.         XtNmenuBackground,
  333.         XtCMenuBackground,
  334.         XtRPixel,
  335.         sizeof(Pixel),
  336.         XtOffsetOf(Gui, menu_bg_pixel),
  337.         XtRString,
  338.         DFLT_MENU_BG_COLOR
  339.     },
  340.     {
  341.         XtNscrollForeground,
  342.         XtCScrollForeground,
  343.         XtRPixel,
  344.         sizeof(Pixel),
  345.         XtOffsetOf(Gui, scroll_fg_pixel),
  346.         XtRString,
  347.         DFLT_SCROLL_FG_COLOR
  348.     },
  349.     {
  350.         XtNscrollBackground,
  351.         XtCScrollBackground,
  352.         XtRPixel,
  353.         sizeof(Pixel),
  354.         XtOffsetOf(Gui, scroll_bg_pixel),
  355.         XtRString,
  356.         DFLT_SCROLL_BG_COLOR
  357.     },
  358. };
  359.  
  360. /*
  361.  * This table holds all the X GUI command line options allowed.  This includes
  362.  * the standard ones so that we can skip them when vim is started without the
  363.  * GUI (but the GUI might start up later).
  364.  * When changing this, also update doc/vim_gui.txt and the usage message!!!
  365.  */
  366. static XrmOptionDescRec cmdline_options[] =
  367. {
  368.     /* We handle these options ourselves */
  369.     {"-bg",                ".background",        XrmoptionSepArg,    NULL},
  370.     {"-background",        ".background",        XrmoptionSepArg,    NULL},
  371.     {"-fg",                ".foreground",        XrmoptionSepArg,    NULL},
  372.     {"-foreground",        ".foreground",        XrmoptionSepArg,    NULL},
  373.     {"-bold",            ".boldColor",        XrmoptionSepArg,    NULL},
  374.     {"-italic",            ".italicColor",        XrmoptionSepArg,    NULL},
  375.     {"-ul",                ".underlineColor",    XrmoptionSepArg,    NULL},
  376.     {"-underline",        ".underlineColor",    XrmoptionSepArg,    NULL},
  377.     {"-cursor",            ".cursorColor",        XrmoptionSepArg,    NULL},
  378.     {"-fn",                ".font",            XrmoptionSepArg,    NULL},
  379.     {"-font",            ".font",            XrmoptionSepArg,    NULL},
  380.     {"-boldfont",        ".boldFont",        XrmoptionSepArg,    NULL},
  381.     {"-italicfont",        ".italicFont",        XrmoptionSepArg,    NULL},
  382.     {"-geom",            ".geometry",        XrmoptionSepArg,    NULL},
  383.     {"-geometry",        ".geometry",        XrmoptionSepArg,    NULL},
  384.     {"-reverse",        "*reverseVideo",    XrmoptionNoArg,        "True"},
  385.     {"-rv",                "*reverseVideo",    XrmoptionNoArg,        "True"},
  386.     {"+reverse",        "*reverseVideo",    XrmoptionNoArg,        "False"},
  387.     {"+rv",                "*reverseVideo",    XrmoptionNoArg,        "False"},
  388.     {"-display",        ".display",            XrmoptionSepArg,    NULL},
  389.     {"-iconic",            "*iconic",            XrmoptionNoArg,        "True"},
  390.     {"-name",            ".name",            XrmoptionSepArg,    NULL},
  391.     {"-bw",                ".borderWidth",        XrmoptionSepArg,    NULL},
  392.     {"-borderwidth",    ".borderWidth",        XrmoptionSepArg,    NULL},
  393.     {"-sw",                ".scrollbarWidth",    XrmoptionSepArg,    NULL},
  394.     {"-scrollbarwidth",    ".scrollbarWidth",    XrmoptionSepArg,    NULL},
  395.     {"-mh",                ".menuHeight",        XrmoptionSepArg,    NULL},
  396.     {"-menuheight",        ".menuHeight",        XrmoptionSepArg,    NULL},
  397.     {"-xrm",            NULL,                XrmoptionResArg,    NULL}
  398. };
  399.  
  400. static int gui_argc = 0;
  401. static char **gui_argv = NULL;
  402.  
  403. /*
  404.  * Call-back routines.
  405.  */
  406.  
  407.     void
  408. gui_x11_timer_cb(timed_out, interval_id)
  409.     XtPointer    timed_out;
  410.     XtIntervalId *interval_id;
  411. {
  412.     *((int *)timed_out) = TRUE;
  413. }
  414.  
  415.     void
  416. gui_x11_visibility_cb(w, dud, event, bool)
  417.     Widget        w;
  418.     XtPointer    dud;
  419.     XEvent        *event;
  420.     Boolean        *bool;
  421. {
  422.     if (event->type != VisibilityNotify)
  423.         return;
  424.  
  425.     gui.visibility = event->xvisibility.state;
  426.  
  427.     /*
  428.      * When we do an XCopyArea(), and the window is partially obscured, we want
  429.      * to receive an event to tell us whether it worked or not.
  430.      */
  431.     XSetGraphicsExposures(gui.dpy, gui.text_gc,
  432.         gui.visibility != VisibilityUnobscured);
  433. }
  434.  
  435.     void
  436. gui_x11_expose_cb(w, dud, event, bool)
  437.     Widget        w;
  438.     XtPointer    dud;
  439.     XEvent        *event;
  440.     Boolean        *bool;
  441. {
  442.     XExposeEvent    *gevent;
  443.  
  444.     if (event->type != Expose)
  445.         return;
  446.  
  447.     gevent = (XExposeEvent *)event;
  448.     gui_redraw(gevent->x, gevent->y, gevent->width, gevent->height);
  449.  
  450.     /* Clear the border areas if needed */
  451.     if (gevent->x < FILL_X(0))
  452.         XClearArea(gui.dpy, gui.wid, 0, 0, FILL_X(0), 0, False);
  453.     if (gevent->y < FILL_Y(0))
  454.         XClearArea(gui.dpy, gui.wid, 0, 0, 0, FILL_Y(0), False);
  455.     if (gevent->x > FILL_X(Columns))
  456.         XClearArea(gui.dpy, gui.wid, FILL_X(Columns), 0, 0, 0, False);
  457.     if (gevent->y > FILL_Y(Rows))
  458.         XClearArea(gui.dpy, gui.wid, 0, FILL_Y(Rows), 0, 0, False);
  459.  
  460.     if (gui.selection.state != SELECT_CLEARED)
  461.         gui_x11_redraw_selection(gevent->x, gevent->y, gevent->width,
  462.                 gevent->height);
  463. }
  464.  
  465.     void
  466. gui_x11_resize_window_cb(w, dud, event, bool)
  467.     Widget        w;
  468.     XtPointer    dud;
  469.     XEvent        *event;
  470.     Boolean        *bool;
  471. {
  472.     if (event->type != ConfigureNotify)
  473.         return;
  474.  
  475.     gui_resize_window(event->xconfigure.width, event->xconfigure.height);
  476.  
  477.     /* Make sure the border strips on the right and bottom get cleared. */
  478.     XClearArea(gui.dpy, gui.wid,    FILL_X(Columns), 0, 0, 0, False);
  479.     XClearArea(gui.dpy, gui.wid, 0, FILL_Y(Rows),       0, 0, False);
  480. }
  481.  
  482.     void
  483. gui_x11_focus_change_cb(w, data, event, bool)
  484.     Widget        w;
  485.     XtPointer    data;
  486.     XEvent        *event;
  487.     Boolean        *bool;
  488. {
  489.     if (event->type == FocusIn)
  490.         gui.in_focus = TRUE;
  491.     else
  492.         gui.in_focus = FALSE;
  493.     if (gui.row == gui.cursor_row && gui.col == gui.cursor_col)
  494.         INVALIDATE_CURSOR();
  495.     gui_update_cursor();
  496. }
  497.  
  498.     void
  499. gui_x11_key_hit_cb(w, dud, event, bool)
  500.     Widget        w;
  501.     XtPointer    dud;
  502.     XEvent        *event;
  503.     Boolean        *bool;
  504. {
  505.     XKeyPressedEvent    *ev_press;
  506.     char_u    string[3], string2[3];
  507.     KeySym    key_sym;
  508.     int        num, i;
  509.  
  510.     ev_press = (XKeyPressedEvent *)event;
  511.  
  512.     num = XLookupString(ev_press, (char *)string, sizeof(string),
  513.             &key_sym, NULL);
  514.     
  515.     if (key_sym == XK_space)
  516.         string[0] = ' ';        /* Otherwise Ctrl-Space doesn't work */
  517.  
  518.     /* Check for Alt/Meta key (Mod1Mask) */
  519.     if (num == 1 && (ev_press->state & Mod1Mask))
  520.     {
  521.         /* 
  522.          * Before we set the 8th bit, check to make sure the user doesn't
  523.          * already have a mapping defined for this sequence. We determine this
  524.          * by checking to see if the input would be the same without the
  525.          * Alt/Meta key.
  526.          */
  527.         ev_press->state &= ~Mod1Mask;
  528.         if (XLookupString(ev_press, (char *)string2, sizeof(string2),
  529.                 &key_sym, NULL) == 1 && string[0] == string2[0])
  530.             string[0] |= 0x80;
  531.         ev_press->state |= Mod1Mask;
  532.     }
  533.  
  534. #if 0
  535.     if (num == 1 && string[0] == CSI) /* this doesn't work yet */
  536.     {
  537.         string[1] = CSI;
  538.         string[2] = CSI;
  539.         num = 3;
  540.     }
  541. #endif
  542.  
  543.     /* Check for special keys, making sure BS and DEL are recognised. */
  544.     if (num == 0 || key_sym == XK_BackSpace || key_sym == XK_Delete)
  545.     {
  546.         for (i = 0; special_keys[i].key_sym != (KeySym)0; i++)
  547.         {
  548.             if (special_keys[i].key_sym == key_sym)
  549.             {
  550.                 string[0] = CSI;
  551.                 string[1] = special_keys[i].vim_code0;
  552.                 string[2] = special_keys[i].vim_code1;
  553.                 num = 3;
  554.             }
  555.         }
  556.     }
  557.  
  558.     /* Unrecognised key */
  559.     if (num == 0)
  560.         return;
  561.  
  562.     /* Special keys (and a few others) may have modifiers */
  563.     if (num == 3 || key_sym == XK_space || key_sym == XK_Tab
  564.         || key_sym == XK_Return || key_sym == XK_Linefeed
  565.         || key_sym == XK_Escape)
  566.     {
  567.         string2[0] = CSI;
  568.         string2[1] = KS_MODIFIER;
  569.         string2[2] = 0;
  570.         if (ev_press->state & ShiftMask)
  571.             string2[2] |= MOD_MASK_SHIFT;
  572.         if (ev_press->state & ControlMask)
  573.             string2[2] |= MOD_MASK_CTRL;
  574.         if (ev_press->state & Mod1Mask)
  575.             string2[2] |= MOD_MASK_ALT;
  576.         if (string2[2] != 0)
  577.             add_to_input_buf(string2, 3);
  578.     }
  579.     if (num == 1 && string[0] == Ctrl('C'))
  580.     {
  581.         trash_input_buf();
  582.         got_int = TRUE;
  583.     }
  584.     add_to_input_buf(string, num);
  585. }
  586.  
  587.     void
  588. gui_x11_mouse_cb(w, dud, event, bool)
  589.     Widget        w;
  590.     XtPointer    dud;
  591.     XEvent        *event;
  592.     Boolean        *bool;
  593. {
  594.     static XtIntervalId timer = (XtIntervalId)0;
  595.     static int    timed_out = TRUE;
  596.  
  597.     int            button;
  598.     int            repeated_click = FALSE;
  599.     int            x, y;
  600.     int_u        x_modifiers;
  601.     int_u        vim_modifiers;
  602.     int            checkfor;
  603.  
  604.     if (event->type == MotionNotify)
  605.     {
  606.         x = event->xmotion.x;
  607.         y = event->xmotion.y;
  608.         button = MOUSE_DRAG;
  609.         x_modifiers = event->xmotion.state;
  610.     }
  611.     else
  612.     {
  613.         x = event->xbutton.x;
  614.         y = event->xbutton.y;
  615.         if (event->type == ButtonPress)
  616.         {
  617.             /* Handle multiple clicks */
  618.             if (!timed_out)
  619.             {
  620.                 XtRemoveTimeOut(timer);
  621.                 repeated_click = TRUE;
  622.             }
  623.             timed_out = FALSE;
  624.             timer = XtAppAddTimeOut(app_context, (long_u)p_mouset,
  625.                         gui_x11_timer_cb, &timed_out);
  626.             switch (event->xbutton.button)
  627.             {
  628.                 case Button1:    button = MOUSE_LEFT;    break;
  629.                 case Button2:    button = MOUSE_MIDDLE;    break;
  630.                 case Button3:    button = MOUSE_RIGHT;    break;
  631.                 default:
  632.                     return;        /* Unknown button */
  633.             }
  634.         }
  635.         else if (event->type == ButtonRelease)
  636.             button = MOUSE_RELEASE;
  637.         else
  638.             return;        /* Unknown mouse event type */
  639.  
  640.         x_modifiers = event->xbutton.state;
  641.     }
  642.  
  643.     vim_modifiers = 0x0;
  644.     if (x_modifiers & ShiftMask)
  645.         vim_modifiers |= MOUSE_SHIFT;
  646.     if (x_modifiers & ControlMask)
  647.         vim_modifiers |= MOUSE_CTRL;
  648.     if (x_modifiers & Mod1Mask)        /* Alt or Meta key */
  649.         vim_modifiers |= MOUSE_ALT;
  650.  
  651.     /* If an x11 selection is in progress, finish it */
  652.     if (gui.selection.state == SELECT_IN_PROGRESS)
  653.     {
  654.         gui_x11_process_selection(button, x, y, repeated_click, vim_modifiers);
  655.         return;
  656.     }
  657.  
  658.     /* Determine which mouse settings to look for based on the current mode */
  659.     switch (State)
  660.     {
  661.         case NORMAL_BUSY:
  662.         case NORMAL:        checkfor = MOUSE_NORMAL;    break;
  663.         case VISUAL:        checkfor = MOUSE_VISUAL;    break;
  664.         case REPLACE:
  665.         case INSERT:        checkfor = MOUSE_INSERT;    break;
  666.         case HITRETURN:        checkfor = MOUSE_RETURN;    break;
  667.  
  668.             /*
  669.              * On the command line, use the X11 selection on all lines but the
  670.              * command line.
  671.              */
  672.         case CMDLINE:        
  673.             if (Y_2_ROW(y) < cmdline_row)
  674.                 checkfor = ' ';
  675.             else
  676.                 checkfor = MOUSE_COMMAND;
  677.             break;
  678.  
  679.         default:
  680.             checkfor = ' ';
  681.             break;
  682.     };
  683.     /*
  684.      * Allow selection of text in the command line in "normal" modes.
  685.      */
  686.     if ((State == NORMAL || State == NORMAL_BUSY ||
  687.                                        State == INSERT || State == REPLACE) &&
  688.                                             Y_2_ROW(y) >= gui.num_rows - p_ch)
  689.         checkfor = ' ';
  690.  
  691.     /*
  692.      * If the mouse settings say to not use the mouse, use the X11 selection.
  693.      * But if Visual is active, assume that only the Visual area will be
  694.      * selected.
  695.      */
  696.     if (!mouse_has(checkfor) && !VIsual_active)
  697.     {
  698.         /* If the selection is done, allow the right button to extend it */
  699.         if (gui.selection.state == SELECT_DONE && button == MOUSE_RIGHT)
  700.         {
  701.             gui_x11_process_selection(button, x, y, repeated_click,
  702.                     vim_modifiers);
  703.             return;
  704.         }
  705.  
  706.         /* Allow the left button to start the selection */
  707.         else if (button == MOUSE_LEFT)
  708.         {
  709.             gui_x11_start_selection(button, x, y, repeated_click,
  710.                     vim_modifiers);
  711.             return;
  712.         }
  713.     }
  714.  
  715.     if (gui.selection.state != SELECT_CLEARED)
  716.         gui_mch_clear_selection();
  717.     gui_send_mouse_event(button, x, y, repeated_click, vim_modifiers);
  718. }
  719.  
  720. /*
  721.  * End of call-back routines
  722.  */
  723.  
  724. /*
  725.  * Parse the GUI related command-line arguments.  Any arguments used are
  726.  * deleted from argv, and *argc is decremented accordingly.  This is called
  727.  * when vim is started, whether or not the GUI has been started.
  728.  */
  729.     void
  730. gui_mch_prepare(argc, argv)
  731.     int        *argc;
  732.     char    **argv;
  733. {
  734.     int        arg;
  735.     int        i;
  736.  
  737.     /*
  738.      * Move all the entries in argv which are relevant to X into gui_argv.
  739.      */
  740.     gui_argc = 0;
  741.     gui_argv = (char **)lalloc(*argc * sizeof(char *), FALSE);
  742.     if (gui_argv == NULL)
  743.         return;
  744.     gui_argv[gui_argc++] = argv[0];
  745.     arg = 1;
  746.     while (arg < *argc)
  747.     {
  748.         /* Look for argv[arg] in cmdline_options[] table */
  749.         for (i = 0; i < XtNumber(cmdline_options); i++)
  750.             if (strcmp(argv[arg], cmdline_options[i].option) == 0)
  751.                 break;
  752.  
  753.         if (i < XtNumber(cmdline_options))
  754.         {
  755.             /* Found match in table, so move it into gui_argv */
  756.             gui_argv[gui_argc++] = argv[arg];
  757.             if (--*argc > arg)
  758.             {
  759.                 vim_memmove(&argv[arg], &argv[arg + 1], (*argc - arg)
  760.                                                     * sizeof(char *));
  761.                 if (cmdline_options[i].argKind != XrmoptionNoArg)
  762.                 {
  763.                     /* Move the options argument as well */
  764.                     gui_argv[gui_argc++] = argv[arg];
  765.                     if (--*argc > arg)
  766.                         vim_memmove(&argv[arg], &argv[arg + 1], (*argc - arg)
  767.                                                             * sizeof(char *));
  768.                 }
  769.             }
  770.         }
  771.         else
  772.             arg++;
  773.     }
  774. }
  775.  
  776. /*
  777.  * Initialise the X GUI.  Create all the windows, set up all the call-backs
  778.  * etc.
  779.  */
  780.     int
  781. gui_mch_init()
  782. {
  783.     Widget        AppShell;
  784.     long_u        gc_mask;
  785.     XGCValues    gc_vals;
  786.     Pixel        tmp_pixel;
  787.     int            x, y, mask;
  788.     unsigned    w, h;
  789.  
  790.     XtToolkitInitialize();
  791.     app_context = XtCreateApplicationContext();
  792.     gui.dpy = XtOpenDisplay(app_context, 0, VIM_NAME, VIM_CLASS,
  793.         cmdline_options, XtNumber(cmdline_options),
  794. #ifndef XtSpecificationRelease
  795.         (Cardinal*)&gui_argc, gui_argv);
  796. #else
  797. #if XtSpecificationRelease == 4
  798.         (Cardinal*)&gui_argc, gui_argv);
  799. #else
  800.         &gui_argc, gui_argv);
  801. #endif
  802. #endif
  803.     
  804.     vim_free(gui_argv);
  805.  
  806.     if (gui.dpy == NULL)
  807.     {
  808.         x = full_screen;
  809.         full_screen = FALSE;            /* use fprintf() */
  810.         EMSG("cannot open display");
  811.         full_screen = x;
  812.         return FAIL;
  813.     }
  814.  
  815.     /* Uncomment this to enable synchronous mode for debugging */
  816.     /* XSynchronize(gui.dpy, True); */
  817.  
  818.     /*
  819.      * So converters work.
  820.      */
  821.     XtInitializeWidgetClass(applicationShellWidgetClass);
  822.     XtInitializeWidgetClass(topLevelShellWidgetClass);
  823.  
  824.     /*
  825.      * The applicationShell is created as an unrealized
  826.      * parent for multiple topLevelShells.    The topLevelShells
  827.      * are created as popup children of the applicationShell.
  828.      * This is a recommendation of Paul Asente & Ralph Swick in
  829.      * _X_Window_System_Toolkit_ p. 677.
  830.      */
  831.     AppShell = XtVaAppCreateShell(VIM_NAME, VIM_CLASS,
  832.             applicationShellWidgetClass, gui.dpy,
  833.             NULL);
  834.  
  835.     /*
  836.      * Get the application resources
  837.      */
  838.     XtVaGetApplicationResources(AppShell, &gui,
  839.         vim_resources, XtNumber(vim_resources), NULL);
  840.  
  841.     /*
  842.      * Check validity of resources
  843.      */
  844.     if (gui.border_width < 0)
  845.         gui.border_width = 0;
  846.  
  847.     /* For reverse video, swap foreground and background colours */
  848.     if (gui.rev_video)
  849.     {
  850.         tmp_pixel = gui.norm_pixel;
  851.         gui.norm_pixel = gui.back_pixel;
  852.         gui.back_pixel = tmp_pixel;
  853.     }
  854.  
  855.     /* Create shell widget to put vim in */
  856.     vimShell = XtVaCreatePopupShell("VIM", 
  857.         topLevelShellWidgetClass, AppShell,
  858.         XtNborderWidth, 0,
  859.         NULL);
  860.  
  861.     /*
  862.      * Check that none of the colours are the same as the background color
  863.      */
  864.     if (gui.norm_pixel == gui.back_pixel)
  865.     {
  866.         gui.norm_pixel = gui_x11_get_color((char_u *)"White");
  867.         if (gui.norm_pixel == gui.back_pixel)
  868.             gui.norm_pixel = gui_x11_get_color((char_u *)"Black");
  869.     }
  870.     if (gui.bold_pixel == gui.back_pixel)
  871.         gui.bold_pixel = gui.norm_pixel;
  872.     if (gui.ital_pixel == gui.back_pixel)
  873.         gui.ital_pixel = gui.norm_pixel;
  874.     if (gui.underline_pixel == gui.back_pixel)
  875.         gui.underline_pixel = gui.norm_pixel;
  876.     if (gui.cursor_pixel == gui.back_pixel)
  877.         gui.cursor_pixel = gui.norm_pixel;
  878.  
  879.     /*
  880.      * Set up the GCs.    The font attributes will be set in gui_init_font().
  881.      */
  882.  
  883.     gc_mask = GCForeground | GCBackground;
  884.     gc_vals.foreground = gui.norm_pixel;
  885.     gc_vals.background = gui.back_pixel;
  886.     gui.text_gc = XCreateGC(gui.dpy, DefaultRootWindow(gui.dpy), gc_mask,
  887.             &gc_vals);
  888.  
  889.     gc_vals.foreground = gui.back_pixel;
  890.     gc_vals.background = gui.norm_pixel;
  891.     gui.back_gc = XCreateGC(gui.dpy, DefaultRootWindow(gui.dpy), gc_mask,
  892.             &gc_vals);
  893.  
  894.     gc_mask |= GCFunction;
  895.     gc_vals.foreground = gui.norm_pixel ^ gui.back_pixel;
  896.     gc_vals.background = gui.norm_pixel ^ gui.back_pixel;
  897.     gc_vals.function   = GXxor;
  898.     gui.invert_gc = XCreateGC(gui.dpy, DefaultRootWindow(gui.dpy), gc_mask,
  899.             &gc_vals);
  900.  
  901.     gui.visibility = VisibilityUnobscured;
  902.     gui.selection.atom = XInternAtom(gui.dpy, "VIM_SELECTION", False);
  903.  
  904.     /*
  905.      * Set up the fonts.  This call will also set the window to the correct
  906.      * size for the used font.
  907.      */
  908.     gui.norm_font = NULL;
  909.     gui.bold_font = NULL;
  910.     gui.ital_font = NULL;
  911.     gui.boldital_font = NULL;
  912.     if (gui_init_font() == FAIL)
  913.         return FAIL;
  914.  
  915.     /* Now adapt the supplied(?) geometry-settings */
  916.     /* Added by Kjetil Jacobsen <kjetilja@stud.cs.uit.no> */
  917.     if (gui.geom != NULL && *gui.geom != NUL)
  918.     {
  919.         mask = XParseGeometry((char *)gui.geom, &x, &y, &w, &h);
  920.         if (mask & WidthValue)
  921.             Columns = w;
  922.         if (mask & HeightValue)
  923.             Rows = h;
  924.         /*
  925.          * Set the (x,y) position of the main window only if specified in the
  926.          * users geometry, so we get good defaults when they don't. This needs
  927.          * to be done before the shell is popped up.
  928.          */
  929.         if (mask & (XValue|YValue))
  930.             XtVaSetValues(vimShell, XtNgeometry, gui.geom, NULL);
  931.     }
  932.     gui.num_cols = Columns;
  933.     gui.num_rows = Rows;
  934.     gui_reset_scroll_region();
  935.  
  936.     gui_mch_create_widgets();
  937.  
  938.     /* Configure the desired scrollbars */
  939.     gui_init_which_components(NULL);
  940.  
  941.     /* Actually open the window */
  942.     XtPopup(vimShell, XtGrabNone);
  943.  
  944.     gui_mch_set_winsize();
  945.  
  946.     gui.wid = gui_mch_get_wid();
  947.  
  948.     /* Add a callback for the Close item on the window managers menu */
  949.     Atom_WM_DELETE_WINDOW = XInternAtom(gui.dpy, "WM_DELETE_WINDOW", False);
  950.     XSetWMProtocols(gui.dpy, XtWindow(vimShell), &Atom_WM_DELETE_WINDOW, 1);
  951.     XtAddEventHandler(vimShell, NoEventMask, True, gui_x11_wm_protocol_handler,
  952.                                                              NULL);
  953.  
  954. #ifdef ENABLE_EDITRES
  955.     /*
  956.      * Enable editres protocol (see editres(1))
  957.      * Usually will need to add -lXmu to the linker line as well.
  958.      */
  959.     {
  960.         extern void _XEditResCheckMessages();
  961.         XtAddEventHandler(vimShell, 0, True, _XEditResCheckMessages,
  962.                 (XtPointer)NULL);
  963.     }
  964. #endif
  965.  
  966.     return OK;
  967. }
  968.  
  969.     void
  970. gui_mch_exit()
  971. {
  972.     XtCloseDisplay(gui.dpy);
  973. }
  974.  
  975. /*
  976.  * While unmanaging and re-managing scrollbars etc, we don't want the resize
  977.  * callback to be called, so this function is used to disable this callback,
  978.  * and then re-enable it afterwards.  XSync() makes sure we don't register the
  979.  * callback before the server has finished processing any resize events we have
  980.  * created, otherwise we can get in a loop where the window flashes between two
  981.  * sizes, since resize_window_cb() calls set_winsize().
  982.  */
  983.     void
  984. gui_x11_use_resize_callback(widget, enabled)
  985.     Widget    widget;
  986.     int        enabled;
  987. {
  988.     if (enabled)
  989.     {
  990.         XSync(gui.dpy, False);
  991.         XtAddEventHandler(widget, StructureNotifyMask, FALSE,
  992.             gui_x11_resize_window_cb, (XtPointer)0);
  993.         XtAddEventHandler(widget, ExposureMask, FALSE, gui_x11_expose_cb,
  994.             (XtPointer)0);
  995.     }
  996.     else
  997.     {
  998.         XtRemoveEventHandler(widget, StructureNotifyMask, FALSE,
  999.             gui_x11_resize_window_cb, (XtPointer)0);
  1000.         XtRemoveEventHandler(widget, ExposureMask, FALSE, gui_x11_expose_cb,
  1001.             (XtPointer)0);
  1002.         XSync(gui.dpy, False);
  1003.     }
  1004. }
  1005.  
  1006. /*
  1007.  * Initialise vim to use the font with the given name.    Return FAIL if the font
  1008.  * could not be loaded, OK otherwise.
  1009.  */
  1010.     int
  1011. gui_mch_init_font(font_name)
  1012.     char_u        *font_name;
  1013. {
  1014.     XFontStruct    *font = NULL;
  1015.  
  1016.     if (font_name == NULL)
  1017.     {
  1018.         /*
  1019.          * If none of the fonts in 'font' could be loaded, try the one set in
  1020.          * the X resource, and finally just try using DFLT_FONT, which will
  1021.          * hopefully always be there.
  1022.          */
  1023.         font = XLoadQueryFont(gui.dpy, (char *)gui.dflt_font);
  1024.         if (font == NULL)
  1025.             font_name = (char_u *)DFLT_FONT;
  1026.         font_name = (char_u *)DFLT_FONT;
  1027.     }
  1028.     if (font == NULL)
  1029.         font = XLoadQueryFont(gui.dpy, (char *)font_name);
  1030.     if (font == NULL)
  1031.         return FAIL;
  1032.     if (font->max_bounds.width != font->min_bounds.width)
  1033.     {
  1034.         sprintf((char *)IObuff, "Font \"%s\" is not fixed-width",
  1035.                                                            (char *)font_name);
  1036.         emsg(IObuff);
  1037.         XFreeFont(gui.dpy, font);
  1038.         return FAIL;
  1039.     }
  1040.     if (gui.norm_font != NULL)
  1041.         XFreeFont(gui.dpy, gui.norm_font);
  1042.     gui.norm_font = font;
  1043.     gui.char_width = font->max_bounds.width;
  1044.     gui.char_height = font->ascent + font->descent;
  1045.     gui.char_ascent = font->ascent;
  1046.  
  1047. #ifdef DEBUG
  1048.     printf("Font Information for '%s':\n", font_name);
  1049.     printf("  w = %d, h = %d, ascent = %d, descent = %d\n", gui.char_width,
  1050.            gui.char_height, gui.char_ascent, font->descent);
  1051.     printf("  max ascent = %d, max descent = %d, max h = %d\n",
  1052.            font->max_bounds.ascent, font->max_bounds.descent,
  1053.            font->max_bounds.ascent + font->max_bounds.descent);
  1054.     printf("  min lbearing = %d, min rbearing = %d\n",
  1055.            font->min_bounds.lbearing, font->min_bounds.rbearing);
  1056.     printf("  max lbearing = %d, max rbearing = %d\n",
  1057.            font->max_bounds.lbearing, font->max_bounds.rbearing);
  1058.     printf("  leftink = %d, rightink = %d\n",
  1059.            (font->min_bounds.lbearing < 0),
  1060.            (font->max_bounds.rbearing > gui.char_width));
  1061.     printf("\n");
  1062. #endif
  1063.  
  1064.     /*
  1065.      * Try to load other fonts for bold, italic, and bold-italic.
  1066.      * We should also try to work out what font to use for these when they are
  1067.      * not specified by X resources, but we don't yet.
  1068.      */
  1069.     if (gui.dflt_bold_fn != NULL && *gui.dflt_bold_fn != NUL)
  1070.         gui.bold_font = XLoadQueryFont(gui.dpy, (char *)gui.dflt_bold_fn);
  1071.     if (gui.dflt_ital_fn != NULL && *gui.dflt_ital_fn != NUL)
  1072.         gui.ital_font = XLoadQueryFont(gui.dpy, (char *)gui.dflt_ital_fn);
  1073.     if (gui.dflt_boldital_fn != NULL && *gui.dflt_boldital_fn != NUL)
  1074.         gui.boldital_font = XLoadQueryFont(gui.dpy, (char *)gui.dflt_boldital_fn);
  1075.  
  1076.     /* Must set the appropriate fonts for all the GCs */
  1077.     gui_x11_set_font(gui.text_gc, gui.norm_font->fid);
  1078.     gui_x11_set_font(gui.back_gc, gui.norm_font->fid);
  1079.  
  1080.     /*
  1081.      * Set the window sizes if the window is already visible.
  1082.      */
  1083.     if (vimShell != (Widget)NULL && XtIsRealized(vimShell))
  1084.     {
  1085.         WIN        *wp;
  1086.  
  1087.         gui_mch_set_winsize();
  1088.  
  1089.         /* Force the scrollbar heights to get updated when a font is changed */
  1090.         if (gui.which_scrollbars[SB_LEFT])
  1091.         {
  1092.             for (wp = firstwin; wp; wp = wp->w_next)
  1093.                 wp->w_scrollbar.update[SB_LEFT] = SB_UPDATE_HEIGHT;
  1094.             gui_mch_update_scrollbars(SB_UPDATE_HEIGHT, SB_LEFT);
  1095.         }
  1096.         if (gui.which_scrollbars[SB_RIGHT])
  1097.         {
  1098.             for (wp = firstwin; wp; wp = wp->w_next)
  1099.                 wp->w_scrollbar.update[SB_RIGHT] = SB_UPDATE_HEIGHT;
  1100.             gui_mch_update_scrollbars(SB_UPDATE_HEIGHT, SB_RIGHT);
  1101.         }
  1102.     }
  1103.  
  1104.     return OK;
  1105. }
  1106.  
  1107. /*
  1108.  * Return OK if the key with the termcap name "name" is supported.
  1109.  */
  1110.     int
  1111. gui_mch_haskey(name)
  1112.     char_u    *name;
  1113. {
  1114.     int i;
  1115.  
  1116.     for (i = 0; special_keys[i].key_sym != (KeySym)0; i++)
  1117.         if (name[0] == special_keys[i].vim_code0 &&
  1118.                                          name[1] == special_keys[i].vim_code1)
  1119.             return OK;
  1120.     return FAIL;
  1121. }
  1122.  
  1123. /*
  1124.  * Return the text window-id and display.  Only required for X-based GUI's
  1125.  */
  1126.     int
  1127. gui_get_x11_windis(win, dis)
  1128.     Window    *win;
  1129.     Display    **dis;
  1130. {
  1131.     *win = XtWindow(vimShell);
  1132.     *dis = gui.dpy;
  1133.     return OK;
  1134. }
  1135.  
  1136.     void
  1137. gui_mch_beep()
  1138. {
  1139.     XBell(gui.dpy, 0);
  1140. }
  1141.  
  1142.     void
  1143. gui_mch_flash()
  1144. {
  1145.     /* Do a visual beep by reversing the foreground and background colors */
  1146.     XFillRectangle(gui.dpy, gui.wid, gui.invert_gc, 0, 0,
  1147.             FILL_X(Columns) + gui.border_offset,
  1148.             FILL_Y(Rows) + gui.border_offset);
  1149.     XSync(gui.dpy, False);
  1150.     mch_delay(20L, TRUE);        /* wait 1/50 of a second */
  1151.     XFillRectangle(gui.dpy, gui.wid, gui.invert_gc, 0, 0,
  1152.             FILL_X(Columns) + gui.border_offset,
  1153.             FILL_Y(Rows) + gui.border_offset);
  1154. }
  1155.  
  1156. /*
  1157.  * Iconify the GUI window.
  1158.  */
  1159.     void
  1160. gui_mch_iconify()
  1161. {
  1162.     XIconifyWindow(gui.dpy, XtWindow(vimShell), DefaultScreen(gui.dpy));
  1163. }
  1164.  
  1165. /*
  1166.  * Return the Pixel value (color) for the given color name.  This routine was
  1167.  * pretty much taken from example code in the Silicon Graphics OSF/Motif
  1168.  * Programmer's Guide.
  1169.  */
  1170.     static Pixel
  1171. gui_x11_get_color(name)
  1172.     char_u *name;
  1173. {
  1174.     XrmValue    from, to;
  1175.  
  1176.     from.size = STRLEN(name) + 1;
  1177.     if (from.size < sizeof(String))
  1178.         from.size = sizeof(String);
  1179.     from.addr = (char *)name;
  1180.     to.addr = NULL;
  1181.     XtConvert(vimShell, XtRString, &from, XtRPixel, &to);
  1182.     if (to.addr != NULL)
  1183.         return (Pixel) *((Pixel *)to.addr);
  1184.     else
  1185.         return (Pixel)NULL;
  1186. }
  1187.  
  1188. /*
  1189.  * Set the current text font (gc should be either gui.text_gc or gui.back_gc)
  1190.  */
  1191.     static void
  1192. gui_x11_set_font(gc, fid)
  1193.     GC        gc;
  1194.     Font    fid;
  1195. {
  1196.     static Font prev_text_fid = (Font) -1;
  1197.     static Font prev_back_fid = (Font) -1;
  1198.  
  1199.     if (gc == gui.text_gc && fid != prev_text_fid)
  1200.     {
  1201.         XSetFont(gui.dpy, gc, fid);
  1202.         prev_text_fid = fid;
  1203.     }
  1204.     else if (gc == gui.back_gc && fid != prev_back_fid)
  1205.     {
  1206.         XSetFont(gui.dpy, gc, fid);
  1207.         prev_back_fid = fid;
  1208.     }
  1209. }
  1210.  
  1211. /*
  1212.  * Set the current text color (gc should be either gui.text_gc or gui.back_gc)
  1213.  */
  1214.     static void
  1215. gui_x11_set_color(gc, pixel)
  1216.     GC        gc;
  1217.     Pixel    pixel;
  1218. {
  1219.     static Pixel prev_text_pixel = (Pixel) -1;
  1220.     static Pixel prev_back_pixel = (Pixel) -1;
  1221.  
  1222.     if (gc == gui.text_gc && pixel != prev_text_pixel)
  1223.     {
  1224.         XSetForeground(gui.dpy, gc, pixel);
  1225.         prev_text_pixel = pixel;
  1226.     }
  1227.     else if (gc == gui.back_gc && pixel != prev_back_pixel)
  1228.     {
  1229.         XSetBackground(gui.dpy, gc, pixel);
  1230.         prev_back_pixel = pixel;
  1231.     }
  1232. }
  1233.  
  1234. /*
  1235.  * Draw the cursor.
  1236.  */
  1237.     void
  1238. gui_mch_draw_cursor()
  1239. {
  1240.     /* Only write to the screen after LinePointers[] has been initialized */
  1241.     if (screen_cleared && NextScreen != NULL)
  1242.     {
  1243.         /* Clear the selection if we are about to write over it */
  1244.         if (gui.selection.state == SELECT_DONE
  1245.                 && gui.row >= gui.selection.start.lnum
  1246.                 && gui.row <= gui.selection.end.lnum)
  1247.             gui_mch_clear_selection();
  1248.  
  1249.         if (gui.row < screen_Rows && gui.col < screen_Columns)
  1250.             gui_mch_outstr_nowrap(LinePointers[gui.row] + gui.col,
  1251.                                                               1, FALSE, TRUE);
  1252.         if (!gui.in_focus)
  1253.         {
  1254.             gui_x11_set_color(gui.text_gc, gui.cursor_pixel);
  1255.             XDrawRectangle(gui.dpy, gui.wid, gui.text_gc, FILL_X(gui.col),
  1256.                     FILL_Y(gui.row), gui.char_width - 1, gui.char_height - 1);
  1257.         }
  1258.     }
  1259. }
  1260.  
  1261. /*
  1262.  * Catch up with any queued X events.  This may put keyboard input into the
  1263.  * input buffer, call resize call-backs, trigger timers etc.  If there is
  1264.  * nothing in the X event queue (& no timers pending), then we return
  1265.  * immediately.
  1266.  */
  1267.     void
  1268. gui_mch_update()
  1269. {
  1270.     while(XtAppPending(app_context) && !is_input_buf_full())
  1271.         XtAppProcessEvent(app_context, XtIMAll);
  1272. }
  1273.  
  1274. /*
  1275.  * The main GUI input routine.    Waits for a character from the keyboard.
  1276.  * wtime == -1        Wait forever.
  1277.  * wtime == 0        Don't wait.
  1278.  * wtime > 0        Wait wtime milliseconds for a character.
  1279.  * Returns OK if a character was found to be available within the given time,
  1280.  * or FAIL otherwise.
  1281.  */
  1282.     int
  1283. gui_mch_wait_for_chars(wtime)
  1284.     int        wtime;
  1285. {
  1286.     XtIntervalId    timer = (XtIntervalId)0;
  1287.     XtIntervalId    updatescript_timer = (XtIntervalId)0;
  1288.     /*
  1289.      * Make these static, in case gui_x11_timer_cb is called after leaving
  1290.      * this function (otherwise a random value on the stack may be changed).
  1291.      */
  1292.     static int        timed_out;
  1293.     static int        do_updatescript;
  1294.  
  1295.     timed_out = FALSE;
  1296.     do_updatescript = FALSE;
  1297.  
  1298.     /*
  1299.      * If we're going to wait a bit, update the menus for the current State.
  1300.      */
  1301.     if (wtime != 0)
  1302.         gui_x11_update_menus(0);
  1303.     gui_mch_update();
  1304.     if (!is_input_buf_empty())    /* Got char, return immediately */
  1305.         return OK;
  1306.     else if (wtime == 0)        /* Don't wait for char */
  1307.         return FAIL;
  1308.     else if (wtime > 0)
  1309.     {
  1310.         timer = XtAppAddTimeOut(app_context, (long_u)wtime, gui_x11_timer_cb,
  1311.             &timed_out);
  1312.     }
  1313.     else
  1314.     {
  1315.         /* We are waiting for ever, so update swap files after p_ut msec's */
  1316.         updatescript_timer = XtAppAddTimeOut(app_context, (long_u)p_ut,
  1317.                                           gui_x11_timer_cb, &do_updatescript);
  1318.     }
  1319.  
  1320.     while (!timed_out)
  1321.     {
  1322.         /*
  1323.          * Don't use gui_mch_update() because then we will spin-lock until a
  1324.          * char arrives, instead we use XtAppProcessEvent() to hang until an
  1325.          * event arrives.  No need to check for input_buf_full because we are
  1326.          * returning as soon as it contains a single char.    Note that
  1327.          * XtAppNextEvent() may not be used because it will not return after a
  1328.          * timer event has arrived -- webb
  1329.          */
  1330.         XtAppProcessEvent(app_context, XtIMAll);
  1331.  
  1332.         /*
  1333.          * If no characters arrive within 'updatetime' milli-seconds, flush all
  1334.          * the swap files to disk.
  1335.          */
  1336.         if (do_updatescript)
  1337.         {
  1338.             updatescript(0);
  1339.             do_updatescript = FALSE;
  1340.             updatescript_timer = (XtIntervalId)0;
  1341.         }
  1342.         if (!is_input_buf_empty())
  1343.         {
  1344.             if (timer != (XtIntervalId)0 && !timed_out)
  1345.                 XtRemoveTimeOut(timer);
  1346.             if (updatescript_timer != (XtIntervalId)0 && !do_updatescript)
  1347.                 XtRemoveTimeOut(updatescript_timer);
  1348.             return OK;
  1349.         }
  1350.     }
  1351.     return FAIL;
  1352. }
  1353.  
  1354. /*
  1355.  * Output routines.
  1356.  */
  1357.  
  1358. /* Flush any output to the screen */
  1359.     void
  1360. gui_mch_flush()
  1361. {
  1362.     XFlush(gui.dpy);
  1363. }
  1364.  
  1365. /*
  1366.  * Clear a rectangular region of the screen from text pos (row1, col1) to
  1367.  * (row2, col2) inclusive.
  1368.  */
  1369.     void
  1370. gui_mch_clear_block(row1, col1, row2, col2)
  1371.     int        row1;
  1372.     int        col1;
  1373.     int        row2;
  1374.     int        col2;
  1375. {
  1376.     /* Clear the selection if we are about to write over it */
  1377.     if (gui.selection.state == SELECT_DONE
  1378.             && row2 >= gui.selection.start.lnum
  1379.             && row1 <= gui.selection.end.lnum)
  1380.         gui_mch_clear_selection();
  1381.  
  1382.     XFillRectangle(gui.dpy, gui.wid, gui.back_gc, FILL_X(col1),
  1383.         FILL_Y(row1), (col2 - col1 + 1) * gui.char_width,
  1384.         (row2 - row1 + 1) * gui.char_height);
  1385.  
  1386.     /* Invalidate cursor if it was in this block */
  1387.     if (gui.cursor_row >= row1 && gui.cursor_row <= row2
  1388.      && gui.cursor_col >= col1 && gui.cursor_col <= col2)
  1389.         INVALIDATE_CURSOR();
  1390. }
  1391.  
  1392. /*
  1393.  * Output the given string at the current cursor position.    If the string is
  1394.  * too long to fit on the line, then it is truncated.  wrap_cursor may be
  1395.  * TRUE if the cursor position should be wrapped when the end of the line is
  1396.  * reached, however the string will still be truncated and not continue on the
  1397.  * next line.  is_cursor should only be TRUE when this function is being called
  1398.  * to actually draw the cursor.
  1399.  */
  1400.     void
  1401. gui_mch_outstr_nowrap(s, len, wrap_cursor, is_cursor)
  1402.     char_u    *s;
  1403.     int        len;
  1404.     int        wrap_cursor;
  1405.     int        is_cursor;
  1406. {
  1407.     GC        text_gc;
  1408.     long_u    highlight_mask;
  1409.  
  1410.     if (len == 0)
  1411.         return;
  1412.  
  1413.     if (len < 0)
  1414.         len = STRLEN(s);
  1415.  
  1416.     if (is_cursor && gui.in_focus)
  1417.         highlight_mask = gui.highlight_mask | HL_INVERSE;
  1418.     else
  1419.         highlight_mask = gui.highlight_mask;
  1420.  
  1421.     if (highlight_mask & (HL_INVERSE | HL_STANDOUT))
  1422.         text_gc = gui.back_gc;
  1423.     else
  1424.         text_gc = gui.text_gc;
  1425.  
  1426.     /* Set the font */
  1427.     if ((highlight_mask & (HL_BOLD | HL_STANDOUT)) && gui.bold_font != NULL)
  1428.         if ((highlight_mask & HL_ITAL) && gui.boldital_font != NULL)
  1429.             gui_x11_set_font(text_gc, gui.boldital_font->fid);
  1430.         else
  1431.             gui_x11_set_font(text_gc, gui.bold_font->fid);
  1432.     else if ((highlight_mask & HL_ITAL) && gui.ital_font != NULL)
  1433.         gui_x11_set_font(text_gc, gui.ital_font->fid);
  1434.     else
  1435.         gui_x11_set_font(text_gc, gui.norm_font->fid);
  1436.  
  1437.     /* Set the color */
  1438.     if (is_cursor && gui.in_focus)
  1439.         gui_x11_set_color(text_gc, gui.cursor_pixel);
  1440.     else if (highlight_mask & (HL_BOLD | HL_STANDOUT))
  1441.         gui_x11_set_color(text_gc, gui.bold_pixel);
  1442.     else if (highlight_mask & HL_ITAL)
  1443.         gui_x11_set_color(text_gc, gui.ital_pixel);
  1444.     else if (highlight_mask & HL_UNDERLINE)
  1445.         gui_x11_set_color(text_gc, gui.underline_pixel);
  1446.     else
  1447.         gui_x11_set_color(text_gc, gui.norm_pixel);
  1448.  
  1449.     /* Clear the selection if we are about to write over it */
  1450.     if (gui.selection.state == SELECT_DONE
  1451.             && gui.row >= gui.selection.start.lnum
  1452.             && gui.row <= gui.selection.end.lnum)
  1453.         gui_mch_clear_selection();
  1454.  
  1455.     /* Draw the text */
  1456.     XDrawImageString(gui.dpy, gui.wid, text_gc,
  1457.         TEXT_X(gui.col), TEXT_Y(gui.row), (char *)s, len);
  1458.  
  1459.     /* No bold font, so fake it */
  1460.     if ((highlight_mask & (HL_BOLD | HL_STANDOUT)) && gui.bold_font == NULL)
  1461.         XDrawString(gui.dpy, gui.wid, text_gc,
  1462.             TEXT_X(gui.col) + 1, TEXT_Y(gui.row), (char *)s, len);
  1463.  
  1464.     /* Underline the text */
  1465.     if ((highlight_mask & HL_UNDERLINE)
  1466.      || ((highlight_mask & HL_ITAL) && gui.ital_font == NULL))
  1467.         XDrawLine(gui.dpy, gui.wid, text_gc, FILL_X(gui.col),
  1468.             FILL_Y(gui.row + 1) - 1, FILL_X(gui.col + len) - 1,
  1469.             FILL_Y(gui.row + 1) - 1);
  1470.  
  1471.     if (!is_cursor)
  1472.     {
  1473.         /* Invalidate the old physical cursor position if we wrote over it */
  1474.         if (gui.cursor_row == gui.row && gui.cursor_col >= gui.col
  1475.          && gui.cursor_col < gui.col + len)
  1476.             INVALIDATE_CURSOR();
  1477.  
  1478.         /* Update the cursor position */
  1479.         gui.col += len;
  1480.         if (wrap_cursor && gui.col >= Columns)
  1481.         {
  1482.             gui.col = 0;
  1483.             gui.row++;
  1484.         }
  1485.     }
  1486. }
  1487.  
  1488. /*
  1489.  * Delete the given number of lines from the given row, scrolling up any
  1490.  * text further down within the scroll region.
  1491.  */
  1492.     void
  1493. gui_mch_delete_lines(row, num_lines)
  1494.     int        row;
  1495.     int        num_lines;
  1496. {
  1497.     if (gui.visibility == VisibilityFullyObscured)
  1498.         return;        /* Can't see the window */
  1499.     
  1500.     if (num_lines <= 0)
  1501.         return;
  1502.  
  1503.     if (row + num_lines > gui.scroll_region_bot)
  1504.     {
  1505.         /* Scrolled out of region, just blank the lines out */
  1506.         gui_mch_clear_block(row, 0, gui.scroll_region_bot, Columns - 1);
  1507.     }
  1508.     else
  1509.     {
  1510.         XCopyArea(gui.dpy, gui.wid, gui.wid, gui.text_gc,
  1511.             FILL_X(0), FILL_Y(row + num_lines),
  1512.             gui.char_width * Columns,
  1513.             gui.char_height * (gui.scroll_region_bot - row - num_lines + 1),
  1514.             FILL_X(0), FILL_Y(row));
  1515.  
  1516.         /* Update gui.cursor_row if the cursor scrolled or copied over */
  1517.         if (gui.cursor_row >= row)
  1518.         {
  1519.             if (gui.cursor_row < row + num_lines)
  1520.                 INVALIDATE_CURSOR();
  1521.             else if (gui.cursor_row <= gui.scroll_region_bot)
  1522.                 gui.cursor_row -= num_lines;
  1523.         }
  1524.  
  1525.         gui_mch_clear_block(gui.scroll_region_bot - num_lines + 1, 0,
  1526.             gui.scroll_region_bot, Columns - 1);
  1527.         gui_x11_check_copy_area();
  1528.     }
  1529. }
  1530.  
  1531. /*
  1532.  * Insert the given number of lines before the given row, scrolling down any
  1533.  * following text within the scroll region.
  1534.  */
  1535.     void
  1536. gui_mch_insert_lines(row, num_lines)
  1537.     int        row;
  1538.     int        num_lines;
  1539. {
  1540.     if (gui.visibility == VisibilityFullyObscured)
  1541.         return;        /* Can't see the window */
  1542.     
  1543.     if (num_lines <= 0)
  1544.         return;
  1545.  
  1546.     if (row + num_lines > gui.scroll_region_bot)
  1547.     {
  1548.         /* Scrolled out of region, just blank the lines out */
  1549.         gui_mch_clear_block(row, 0, gui.scroll_region_bot, Columns - 1);
  1550.     }
  1551.     else
  1552.     {
  1553.         XCopyArea(gui.dpy, gui.wid, gui.wid, gui.text_gc,
  1554.             FILL_X(0), FILL_Y(row),
  1555.             gui.char_width * Columns,
  1556.             gui.char_height * (gui.scroll_region_bot - row - num_lines + 1),
  1557.             FILL_X(0), FILL_Y(row + num_lines));
  1558.  
  1559.         /* Update gui.cursor_row if the cursor scrolled or copied over */
  1560.         if (gui.cursor_row >= gui.row)
  1561.         {
  1562.             if (gui.cursor_row <= gui.scroll_region_bot - num_lines)
  1563.                 gui.cursor_row += num_lines;
  1564.             else if (gui.cursor_row <= gui.scroll_region_bot)
  1565.                 INVALIDATE_CURSOR();
  1566.         }
  1567.  
  1568.         gui_mch_clear_block(row, 0, row + num_lines - 1, Columns - 1);
  1569.         gui_x11_check_copy_area();
  1570.     }
  1571. }
  1572.  
  1573. /*
  1574.  * Scroll the text between gui.scroll_region_top & gui.scroll_region_bot by the
  1575.  * number of lines given.  Positive scrolls down (text goes up) and negative
  1576.  * scrolls up (text goes down).
  1577.  */
  1578.     static void
  1579. gui_x11_check_copy_area()
  1580. {
  1581.     XEvent                    event;
  1582.     XGraphicsExposeEvent    *gevent;
  1583.  
  1584.     if (gui.visibility != VisibilityPartiallyObscured)
  1585.         return;
  1586.  
  1587.     XFlush(gui.dpy);
  1588.  
  1589.     /* Wait to check whether the scroll worked or not */
  1590.     for (;;)
  1591.     {
  1592.         if (XCheckTypedEvent(gui.dpy, NoExpose, &event))
  1593.             return;        /* The scroll worked. */
  1594.         
  1595.         if (XCheckTypedEvent(gui.dpy, GraphicsExpose, &event))
  1596.         {
  1597.             gevent = (XGraphicsExposeEvent *)&event;
  1598.             gui_redraw(gevent->x, gevent->y, gevent->width, gevent->height);
  1599.             if (gevent->count == 0)
  1600.                 return;            /* This was the last expose event */
  1601.         }
  1602.         XSync(gui.dpy, False);
  1603.     }
  1604. }
  1605.  
  1606. /*
  1607.  * X Selection stuff, for cutting and pasting text to other windows.
  1608.  */
  1609.  
  1610.     static void
  1611. gui_x11_request_selection_cb(w, success, selection, type, value, length, format)
  1612.     Widget        w;
  1613.     XtPointer    success;
  1614.     Atom        *selection;
  1615.     Atom        *type;
  1616.     XtPointer    value;
  1617.     long_u        *length;
  1618.     int            *format;
  1619. {
  1620.     int        motion_type;
  1621.     long_u    len;
  1622.     char_u    *p;
  1623.  
  1624.     if (value == NULL || *length == 0)
  1625.     {
  1626.         gui_free_selection();    /* ??? */
  1627.         *(int *)success = FALSE;
  1628.         return;
  1629.     }
  1630.     motion_type = MCHAR;
  1631.     p = (char_u *)value;
  1632.     len = *length;
  1633.     if (*type == gui.selection.atom)
  1634.     {
  1635.         motion_type = *p++;
  1636.         len--;
  1637.     }
  1638.     gui_yank_selection(motion_type, p, len);
  1639.  
  1640.     XtFree((char *)value);
  1641.     *(int *)success = TRUE;
  1642. }
  1643.  
  1644.     void
  1645. gui_request_selection()
  1646. {
  1647.     XEvent    event;
  1648.     Atom    type = gui.selection.atom;
  1649.     int        success;
  1650.     int        i;
  1651.  
  1652.     for (i = 0; i < 2; i++)
  1653.     {
  1654.         XtGetSelectionValue(vimShell, XA_PRIMARY, type,
  1655.             gui_x11_request_selection_cb, (XtPointer)&success, CurrentTime);
  1656.  
  1657.         /* Do we need this?: */
  1658.         XFlush(gui.dpy);
  1659.  
  1660.         /*
  1661.          * Wait for result of selection request, otherwise if we type more
  1662.          * characters, then they will appear before the one that requested the
  1663.          * paste!  Don't worry, we will catch up with any other events later.
  1664.          */
  1665.         for (;;)
  1666.         {
  1667.             if (XCheckTypedEvent(gui.dpy, SelectionNotify, &event))
  1668.                 break;
  1669.  
  1670.             /* Do we need this?: */
  1671.             XSync(gui.dpy, False);
  1672.         }
  1673.         XtDispatchEvent(&event);
  1674.  
  1675.         if (success)
  1676.             return;
  1677.         type = XA_STRING;
  1678.     }
  1679. }
  1680.  
  1681.     static Boolean
  1682. gui_x11_convert_selection_cb(w, selection, target, type, value, length, format)
  1683.     Widget        w;
  1684.     Atom        *selection;
  1685.     Atom        *target;
  1686.     Atom        *type;
  1687.     XtPointer    *value;
  1688.     long_u        *length;
  1689.     int            *format;
  1690. {
  1691.     char_u    *string;
  1692.     char_u    *result;
  1693.     int        motion_type;
  1694.  
  1695.     if (!gui.selection.owned)
  1696.         return False;        /* Shouldn't ever happen */
  1697.  
  1698.     if (*target == gui.selection.atom)
  1699.     {
  1700.         gui_get_selection();
  1701.         motion_type = gui_convert_selection(&string, length);
  1702.         if (motion_type < 0)
  1703.             return False;
  1704.  
  1705.         (*length)++;
  1706.         *value = XtMalloc(*length);
  1707.         result = (char_u *)*value;
  1708.         if (result == NULL)
  1709.             return False;
  1710.         result[0] = motion_type;
  1711.         vim_memmove(result + 1, string, (size_t)(*length - 1));
  1712.         *type = *target;
  1713.         *format = 8;        /* 8 bits per char */
  1714.         return True;
  1715.     }
  1716.     else if (*target == XA_STRING)
  1717.     {
  1718.         gui_get_selection();
  1719.         motion_type = gui_convert_selection(&string, length);
  1720.         if (motion_type < 0)
  1721.             return False;
  1722.  
  1723.         *value = XtMalloc(*length);
  1724.         result = (char_u *)*value;
  1725.         if (result == NULL)
  1726.             return False;
  1727.         vim_memmove(result, string, (size_t)(*length));
  1728.         *type = *target;
  1729.         *format = 8;        /* 8 bits per char */
  1730.         return True;
  1731.     }
  1732.     else
  1733.         return False;
  1734. }
  1735.  
  1736.     static void
  1737. gui_x11_lose_ownership_cb(w, selection)
  1738.     Widget    w;
  1739.     Atom    *selection;
  1740. {
  1741.     gui_lose_selection();
  1742. }
  1743.  
  1744.     void
  1745. gui_mch_lose_selection()
  1746. {
  1747.     XtDisownSelection(vimShell, XA_PRIMARY, CurrentTime);
  1748.     gui_mch_clear_selection();
  1749. }
  1750.  
  1751.     int
  1752. gui_mch_own_selection()
  1753. {
  1754.     if (XtOwnSelection(vimShell, XA_PRIMARY, CurrentTime,
  1755.             gui_x11_convert_selection_cb, gui_x11_lose_ownership_cb,
  1756.             NULL) == False)
  1757.         return FAIL;
  1758.     
  1759.     return OK;
  1760. }
  1761.  
  1762. /*
  1763.  * Menu stuff.
  1764.  */
  1765.  
  1766.     void
  1767. gui_x11_menu_cb(w, client_data, call_data)
  1768.     Widget        w;
  1769.     XtPointer    client_data, call_data;
  1770. {
  1771.     gui_menu_cb((GuiMenu *)client_data);
  1772. }
  1773.  
  1774. /*
  1775.  * Used recursively by gui_x11_update_menus (see below)
  1776.  */
  1777.     static void
  1778. gui_x11_update_menus_recurse(menu, mode)
  1779.     GuiMenu    *menu;
  1780.     int        mode;
  1781. {
  1782.     while (menu)
  1783.     {
  1784.         if (menu->modes & mode)
  1785.         {
  1786.             if (vim_strchr(p_guioptions, GO_GREY) != NULL)
  1787.                 XtSetSensitive(menu->id, True);
  1788.             else
  1789.                 XtManageChild(menu->id);
  1790.             gui_x11_update_menus_recurse(menu->children, mode);
  1791.         }
  1792.         else
  1793.         {
  1794.             if (vim_strchr(p_guioptions, GO_GREY) != NULL)
  1795.                 XtSetSensitive(menu->id, False);
  1796.             else
  1797.                 XtUnmanageChild(menu->id);
  1798.         }
  1799.         menu = menu->next;
  1800.     }
  1801. }
  1802.  
  1803. /*
  1804.  * Make sure only the valid menu items appear for this mode.  If
  1805.  * force_menu_update is not TRUE, then we only do this if the mode has changed
  1806.  * since last time.  If "modes" is not 0, then we use these modes instead.
  1807.  */
  1808.     void
  1809. gui_x11_update_menus(modes)
  1810.     int        modes;
  1811. {
  1812.     static int prev_mode = -1;
  1813.  
  1814.     int mode = 0;
  1815.  
  1816.     if (modes != 0x0)
  1817.         mode = modes;
  1818.     else if (VIsual_active)
  1819.         mode = MENU_VISUAL_MODE;
  1820.     else if (State & NORMAL)
  1821.         mode = MENU_NORMAL_MODE;
  1822.     else if (State & INSERT)
  1823.         mode = MENU_INSERT_MODE;
  1824.     else if (State & CMDLINE)
  1825.         mode = MENU_CMDLINE_MODE;
  1826.  
  1827.     if (force_menu_update || mode != prev_mode)
  1828.     {
  1829.         gui_x11_update_menus_recurse(gui.root_menu, mode);
  1830.         prev_mode = mode;
  1831.         force_menu_update = FALSE;
  1832.     }
  1833. }
  1834.  
  1835. /*
  1836.  * Function called when window closed.  Preserve files and exit.
  1837.  * Should put up a requester!
  1838.  */
  1839. /*ARGSUSED*/
  1840.     static void
  1841. gui_x11_wm_protocol_handler(w, client_data, event, bool)
  1842.     Widget        w;
  1843.     XtPointer    client_data;
  1844.     XEvent        *event;
  1845.     Boolean        *bool;
  1846. {
  1847.     /*
  1848.      * On some HPUX system with Motif 1.2 this function is somehow called when
  1849.      * starting up.  This if () avoids an unexpected exit.
  1850.      */
  1851.     if (event->type != ClientMessage ||
  1852.            ((XClientMessageEvent *)event)->data.l[0] != Atom_WM_DELETE_WINDOW)
  1853.         return;
  1854.  
  1855.     STRCPY(IObuff, "Vim: Window closed\n");
  1856.     preserve_exit();                /* preserve files and exit */
  1857. }
  1858.  
  1859. /*
  1860.  * Compare two screen positions ala strcmp()
  1861.  */
  1862.     static int
  1863. gui_x11_compare_pos(row1, col1, row2, col2)
  1864.     short_u        row1;
  1865.     short_u        col1;
  1866.     short_u        row2;
  1867.     short_u        col2;
  1868. {
  1869.     if (row1 > row2) return( 1);
  1870.     if (row1 < row2) return(-1);
  1871.     if (col1 > col2) return( 1);
  1872.     if (col1 < col2) return(-1);
  1873.                      return( 0);
  1874. }
  1875.  
  1876. /*
  1877.  * Start out the selection
  1878.  */
  1879.     void
  1880. gui_x11_start_selection(button, x, y, repeated_click, modifiers)
  1881.     int     button;
  1882.     int     x;
  1883.     int     y;
  1884.     int     repeated_click;
  1885.     int_u   modifiers;
  1886. {
  1887.     GuiSelection    *gs = &gui.selection;
  1888.  
  1889.     if (gs->state == SELECT_DONE)
  1890.         gui_mch_clear_selection();
  1891.     
  1892.     gs->start.lnum    = Y_2_ROW(y);
  1893.     gs->start.col    = X_2_COL(x);
  1894.     gs->end            = gs->start;
  1895.     gs->origin_row    = gs->start.lnum;
  1896.     gs->state        = SELECT_IN_PROGRESS;
  1897.  
  1898.     if (repeated_click)
  1899.     {
  1900.         if (++(gs->mode) > SELECT_MODE_LINE)
  1901.             gs->mode = SELECT_MODE_CHAR;
  1902.     }
  1903.     else
  1904.         gs->mode = SELECT_MODE_CHAR;
  1905.  
  1906.     /* clear the cursor until the selection is made */
  1907.     gui_undraw_cursor();
  1908.  
  1909.     switch (gs->mode)
  1910.     {
  1911.         case SELECT_MODE_CHAR:
  1912.             gs->origin_start_col = gs->start.col;
  1913.             gs->word_end_col = gui_x11_get_line_end(gs->start.lnum);
  1914.             break;
  1915.  
  1916.         case SELECT_MODE_WORD:
  1917.             gui_x11_get_word_boundaries(gs, gs->start.lnum, gs->start.col);
  1918.             gs->origin_start_col = gs->word_start_col;
  1919.             gs->origin_end_col   = gs->word_end_col;
  1920.  
  1921.             gui_x11_invert_area(gs->start.lnum, gs->word_start_col,
  1922.                                 gs->end.lnum, gs->word_end_col);
  1923.             gs->start.col = gs->word_start_col;
  1924.             gs->end.col   = gs->word_end_col;
  1925.             break;
  1926.  
  1927.         case SELECT_MODE_LINE:
  1928.             gui_x11_invert_area(gs->start.lnum, 0, gs->start.lnum,
  1929.                     gui.num_cols);
  1930.             gs->start.col = 0;
  1931.             gs->end.col   = gui.num_cols;
  1932.             break;
  1933.     }
  1934.  
  1935.     gs->prev = gs->start;
  1936.             
  1937. #ifdef DEBUG_SELECTION
  1938.     printf("Selection started at (%u,%u)\n", gs->start.lnum, gs->start.col);
  1939. #endif
  1940. }
  1941.  
  1942. /*
  1943.  * Continue processing the selection
  1944.  */
  1945.     void
  1946. gui_x11_process_selection(button, x, y, repeated_click, modifiers)
  1947.     int     button;
  1948.     int     x;
  1949.     int     y;
  1950.     int     repeated_click;
  1951.     int_u   modifiers;
  1952. {
  1953.     GuiSelection    *gs = &gui.selection;
  1954.     int                   row, col;
  1955.     int                diff;
  1956.     
  1957.     if (button == MOUSE_RELEASE)
  1958.     {
  1959.         /* Check to make sure we have something selected */
  1960.         if (gs->start.lnum == gs->end.lnum && gs->start.col == gs->end.col)
  1961.         {
  1962.             gui_update_cursor();
  1963.             gs->state = SELECT_CLEARED;
  1964.             return;
  1965.         }
  1966.  
  1967. #ifdef DEBUG_SELECTION
  1968.         printf("Selection ended: (%u,%u) to (%u,%u)\n", gs->start.lnum,
  1969.                 gs->start.col, gs->end.lnum, gs->end.col);
  1970. #endif
  1971.         gui_free_selection();
  1972.         gui_own_selection();
  1973.         gui_x11_yank_selection(gs->start.lnum, gs->start.col, gs->end.lnum,
  1974.                 gs->end.col);
  1975.         gui_update_cursor();
  1976.  
  1977.         gs->state = SELECT_DONE;
  1978.         return;
  1979.     }
  1980.  
  1981.     row = Y_2_ROW(y);
  1982.     col = X_2_COL(x);
  1983.  
  1984.     row = check_row(row);
  1985.     col = check_col(col);
  1986.  
  1987.     if (col == gs->prev.col && row == gs->prev.lnum)
  1988.         return;
  1989.  
  1990.     /*
  1991.      * When extending the selection with the right mouse button, swap the
  1992.      * start and end if the position is before half the selection
  1993.      */
  1994.     if (gs->state == SELECT_DONE && button == MOUSE_RIGHT)
  1995.     {
  1996.         /*
  1997.          * If the click is before the start, or the click is inside the
  1998.          * selection and the start is the closest side, set the origin to the
  1999.          * end of the selection.
  2000.          */
  2001.         if (gui_x11_compare_pos(row, col, gs->start.lnum, gs->start.col) < 0 ||
  2002.                 (gui_x11_compare_pos(row, col, gs->end.lnum, gs->end.col) < 0 &&
  2003.                  (((gs->start.lnum == gs->end.lnum &&
  2004.                     gs->end.col - col > col - gs->start.col)) ||
  2005.                   ((diff = (gs->end.lnum - row) - (row - gs->start.lnum)) > 0 ||
  2006.                    (diff == 0 && col < (gs->start.col + gs->end.col) / 2)))))
  2007.         {
  2008.             gs->origin_row = gs->end.lnum;
  2009.             gs->origin_start_col = gs->end.col - 1;
  2010.             gs->origin_end_col = gs->end.col;
  2011.         }
  2012.         else
  2013.         {
  2014.             gs->origin_row = gs->start.lnum;
  2015.             gs->origin_start_col = gs->start.col;
  2016.             gs->origin_end_col = gs->start.col;
  2017.         }
  2018.         if (gs->mode == SELECT_MODE_WORD)
  2019.         {
  2020.             gui_x11_get_word_boundaries(gs, gs->origin_row,
  2021.                                                         gs->origin_start_col);
  2022.             gs->origin_start_col = gs->word_start_col;
  2023.             gs->origin_end_col   = gs->word_end_col;
  2024.         }
  2025.     }
  2026.  
  2027.     /* set state, for when using the right mouse button */
  2028.     gs->state = SELECT_IN_PROGRESS;
  2029.  
  2030. #ifdef DEBUG_SELECTION
  2031.     printf("Selection extending to (%d,%d)\n", row, col);
  2032. #endif
  2033.  
  2034.     switch (gs->mode)
  2035.     {
  2036.         case SELECT_MODE_CHAR:
  2037.             /* If we're on a different line, find where the line ends */
  2038.             if (row != gs->prev.lnum)
  2039.                 gs->word_end_col = gui_x11_get_line_end(row);
  2040.  
  2041.             /* See if we are before or after the origin of the selection */
  2042.             if (gui_x11_compare_pos(row, col, gs->origin_row,
  2043.                                                    gs->origin_start_col) >= 0)
  2044.             {
  2045.                 if (col >= (int)gs->word_end_col)
  2046.                     gui_x11_update_selection(gs, gs->origin_row,
  2047.                             gs->origin_start_col, row, gui.num_cols);
  2048.                 else
  2049.                     gui_x11_update_selection(gs, gs->origin_row,
  2050.                             gs->origin_start_col, row, col + 1);
  2051.             }
  2052.             else
  2053.             {
  2054.                 if (col >= (int)gs->word_end_col)
  2055.                     gui_x11_update_selection(gs, row, gs->word_end_col,
  2056.                             gs->origin_row, gs->origin_start_col + 1);
  2057.                 else
  2058.                     gui_x11_update_selection(gs, row, col, gs->origin_row,
  2059.                             gs->origin_start_col + 1);
  2060.             }
  2061.             break;
  2062.  
  2063.         case SELECT_MODE_WORD:
  2064.             /* If we are still within the same word, do nothing */
  2065.             if (row == gs->prev.lnum && col >= (int)gs->word_start_col
  2066.                     && col < (int)gs->word_end_col)
  2067.                 return;
  2068.  
  2069.             /* Get new word boundaries */
  2070.             gui_x11_get_word_boundaries(gs, row, col);
  2071.  
  2072.             /* Handle being after the origin point of selection */
  2073.             if (gui_x11_compare_pos(row, col, gs->origin_row,
  2074.                     gs->origin_start_col) >= 0)
  2075.                 gui_x11_update_selection(gs, gs->origin_row,
  2076.                         gs->origin_start_col, row, gs->word_end_col);
  2077.             else
  2078.                 gui_x11_update_selection(gs, row, gs->word_start_col,
  2079.                         gs->origin_row, gs->origin_end_col);
  2080.             break;
  2081.  
  2082.         case SELECT_MODE_LINE:
  2083.             if (row == gs->prev.lnum)
  2084.                 return;
  2085.  
  2086.             if (gui_x11_compare_pos(row, col, gs->origin_row,
  2087.                     gs->origin_start_col) >= 0)
  2088.                 gui_x11_update_selection(gs, gs->origin_row, 0, row,
  2089.                         gui.num_cols);
  2090.             else
  2091.                 gui_x11_update_selection(gs, row, 0, gs->origin_row,
  2092.                         gui.num_cols);
  2093.             break;
  2094.     }
  2095.  
  2096.     gs->prev.lnum = row;
  2097.     gs->prev.col  = col;
  2098.  
  2099. #ifdef DEBUG_SELECTION
  2100.         printf("Selection is: (%u,%u) to (%u,%u)\n", gs->start.lnum,
  2101.                 gs->start.col, gs->end.lnum, gs->end.col);
  2102. #endif
  2103. }
  2104.  
  2105. /*
  2106.  * Called after an Expose event to redraw the selection
  2107.  */
  2108.     void
  2109. gui_x11_redraw_selection(x, y, w, h)
  2110.     int     x;
  2111.     int     y;
  2112.     int     w;
  2113.     int     h;
  2114. {
  2115.     GuiSelection    *gs = &gui.selection;
  2116.     int                row1, col1, row2, col2;
  2117.     int             row;
  2118.     int             start;
  2119.     int             end;
  2120.     
  2121.     if (gs->state == SELECT_CLEARED)
  2122.         return;
  2123.  
  2124.     row1 = Y_2_ROW(y);
  2125.     col1 = X_2_COL(x);
  2126.     row2 = Y_2_ROW(y + h - 1);
  2127.     col2 = X_2_COL(x + w - 1);
  2128.  
  2129.     /* Limit the rows that need to be re-drawn */
  2130.     if (gs->start.lnum > row1)
  2131.         row1 = gs->start.lnum;
  2132.     if (gs->end.lnum < row2)
  2133.         row2 = gs->end.lnum;
  2134.     
  2135.     /* Look at each row that might need to be re-drawn */
  2136.     for (row = row1; row <= row2; row++)
  2137.     {
  2138.         /* For the first selection row, use the starting selection column */
  2139.         if (row == gs->start.lnum)
  2140.             start = gs->start.col;
  2141.         else
  2142.             start = 0;
  2143.  
  2144.         /* For the last selection row, use the ending selection column */
  2145.         if (row == gs->end.lnum)
  2146.             end = gs->end.col;
  2147.         else
  2148.             end = gui.num_cols;
  2149.         
  2150.         if (col1 > start)
  2151.             start = col1;
  2152.  
  2153.         if (col2 < end)
  2154.             end = col2 + 1;
  2155.  
  2156.         if (end > start)
  2157.             invert_rectangle(row, start, 1, end - start);
  2158.     }
  2159. }
  2160.  
  2161. /*
  2162.  * Called from outside to clear selected region from the display
  2163.  */
  2164.     void
  2165. gui_mch_clear_selection()
  2166. {
  2167.     GuiSelection    *gs = &gui.selection;
  2168.  
  2169.     if (gs->state == SELECT_CLEARED)
  2170.         return;
  2171.  
  2172.     gui_x11_invert_area(gs->start.lnum, gs->start.col, gs->end.lnum,
  2173.             gs->end.col);
  2174.     gs->state = SELECT_CLEARED;
  2175. }
  2176.  
  2177. /*
  2178.  * Invert a region of the display between a starting and ending row and column
  2179.  */
  2180.     static void
  2181. gui_x11_invert_area(row1, col1, row2, col2)
  2182.     int        row1;
  2183.     int        col1;
  2184.     int        row2;
  2185.     int        col2;
  2186. {
  2187.     /* Swap the from and to positions so the from is always before */
  2188.     if (gui_x11_compare_pos(row1, col1, row2, col2) > 0)
  2189.     {
  2190.         int tmp_row, tmp_col;
  2191.         tmp_row = row1;
  2192.         tmp_col = col1;
  2193.         row1    = row2;
  2194.         col1    = col2;
  2195.         row2    = tmp_row;
  2196.         col2    = tmp_col;
  2197.     }
  2198.  
  2199.     /* If all on the same line, do it the easy way */
  2200.     if (row1 == row2)
  2201.     {
  2202.         invert_rectangle(row1, col1, 1, col2 - col1);
  2203.         return;
  2204.     }
  2205.  
  2206.     /* Handle a piece of the first line */
  2207.     if (col1 > 0)
  2208.     {
  2209.         invert_rectangle(row1, col1, 1, gui.num_cols - col1);
  2210.         row1++;
  2211.     }
  2212.  
  2213.     /* Handle a piece of the last line */
  2214.     if (col2 < gui.num_cols - 1)
  2215.     {
  2216.         invert_rectangle(row2, 0, 1, col2);
  2217.         row2--;
  2218.     }
  2219.  
  2220.     /* Handle the rectangle thats left */
  2221.     if (row2 >= row1)
  2222.         invert_rectangle(row1, 0, row2 - row1 + 1, gui.num_cols);
  2223. }
  2224.  
  2225. /*
  2226.  * Yank the currently selected area into the special selection buffer so it
  2227.  * will be available for pasting.
  2228.  */
  2229.     static void
  2230. gui_x11_yank_selection(row1, col1, row2, col2)
  2231.     int        row1;
  2232.     int        col1;
  2233.     int        row2;
  2234.     int        col2;
  2235. {
  2236.     char_u    *buffer;
  2237.     char_u    *bufp;
  2238.     int          row;
  2239.     int       start_col;
  2240.     int       end_col;
  2241.     int          line_end_col;
  2242.     int          add_newline_flag = FALSE;
  2243.  
  2244.     /* Create a temporary buffer for storing the text */
  2245.     buffer = lalloc((row2 - row1 + 1) * gui.num_cols + 1, TRUE);
  2246.     if (buffer == NULL)
  2247.         return;                    /* Should there be an error message here? */
  2248.  
  2249.     /* Process each row in the selection */
  2250.     for (bufp = buffer, row = row1; row <= row2; row++)
  2251.     {
  2252.         if (row == row1)
  2253.             start_col = col1;
  2254.         else
  2255.             start_col = 0;
  2256.  
  2257.         if (row == row2)
  2258.             end_col = col2;
  2259.         else
  2260.             end_col = gui.num_cols;
  2261.  
  2262.         line_end_col = gui_x11_get_line_end(row);
  2263.  
  2264.         /* See if we need to nuke some trailing whitespace */
  2265.         if (end_col >= gui.num_cols && (row < row2 || end_col > line_end_col))
  2266.         {
  2267.             /* Get rid of trailing whitespace */
  2268.             end_col = line_end_col;
  2269.             if (end_col < start_col)
  2270.                 end_col = start_col;
  2271.  
  2272.             /* If the last line extended to the end, add an extra newline */
  2273.             if (row == row2)
  2274.                 add_newline_flag = TRUE;
  2275.         }
  2276.  
  2277.         /* If after the first row, we need to always add a newline */
  2278.         if (row > row1)
  2279.             *bufp++ = NL;
  2280.  
  2281.         if (gui.row < screen_Rows && end_col <= screen_Columns)
  2282.         {
  2283.             STRNCPY(bufp, &LinePointers[row][start_col], end_col - start_col);
  2284.             bufp += end_col - start_col;
  2285.         }
  2286.     }
  2287.  
  2288.     /* Add a newline at the end if the selection ended there */
  2289.     if (add_newline_flag)
  2290.         *bufp++ = NL;
  2291.  
  2292.     gui_yank_selection(MCHAR, buffer, bufp - buffer);
  2293.     vim_free(buffer);
  2294. }
  2295.  
  2296. /*
  2297.  * Find the starting and ending positions of the word at the given row and
  2298.  * column.
  2299.  */
  2300.     static void
  2301. gui_x11_get_word_boundaries(gs, row, col)
  2302.     GuiSelection    *gs;
  2303.     int                row;
  2304.     int                col;
  2305. {
  2306.     char    start_class;
  2307.     int        temp_col;
  2308.  
  2309.     if (row >= screen_Rows || col >= screen_Columns)
  2310.         return;
  2311.  
  2312.     start_class = char_class(LinePointers[row][col]);
  2313.  
  2314.     temp_col = col;
  2315.     for ( ; temp_col > 0; temp_col--)
  2316.         if (char_class(LinePointers[row][temp_col - 1]) != start_class)
  2317.             break;
  2318.  
  2319.     gs->word_start_col = temp_col;
  2320.  
  2321.     temp_col = col;
  2322.     for ( ; temp_col < screen_Columns; temp_col++)
  2323.         if (char_class(LinePointers[row][temp_col]) != start_class)
  2324.             break;
  2325.     gs->word_end_col = temp_col;
  2326.  
  2327. #ifdef DEBUG_SELECTION
  2328.     printf("Current word: col %u to %u\n", gs->word_start_col,
  2329.             gs->word_end_col);
  2330. #endif
  2331. }
  2332.  
  2333. /*
  2334.  * Find the column position for the last non-whitespace character on the given
  2335.  * line.
  2336.  */
  2337.     static int
  2338. gui_x11_get_line_end(row)
  2339.     int            row;
  2340. {
  2341.     int        i;
  2342.  
  2343.     if (row >= screen_Rows)
  2344.         return 0;
  2345.     for (i = screen_Columns; i > 0; i--)
  2346.         if (LinePointers[row][i - 1] != ' ')
  2347.             break;
  2348.     return i;
  2349. }
  2350.  
  2351. /*
  2352.  * Update the currently selected region by adding and/or subtracting from the
  2353.  * beginning or end and inverting the changed area(s).
  2354.  */
  2355.     static void
  2356. gui_x11_update_selection(gs, row1, col1, row2, col2)
  2357.     GuiSelection    *gs;
  2358.     int                row1;
  2359.     int                col1;
  2360.     int                row2;
  2361.     int                col2;
  2362. {
  2363.     /* See if we changed at the beginning of the selection */
  2364.     if (row1 != gs->start.lnum || col1 != gs->start.col)
  2365.     {
  2366.         gui_x11_invert_area(row1, col1, gs->start.lnum, gs->start.col);
  2367.         gs->start.lnum = row1;
  2368.         gs->start.col  = col1;
  2369.     }
  2370.  
  2371.     /* See if we changed at the end of the selection */
  2372.     if (row2 != gs->end.lnum || col2 != gs->end.col)
  2373.     {
  2374.         gui_x11_invert_area(row2, col2, gs->end.lnum, gs->end.col);
  2375.         gs->end.lnum = row2;
  2376.         gs->end.col  = col2;
  2377.     }
  2378. }
  2379.